package testing;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.LinkedList;
import java.util.Random;

import com.thesis.algorithm.FileConverter;
import com.thesis.algorithm.HMM;
import com.thesis.algorithm.Sequence;
import com.thesis.algorithm.SequenceElement;
import com.thesis.utils.BusControl;
import com.thesis.utils.CONSTANTS;

public class HMMWrapper {
	BusControl busController  = new BusControl();
	FileConverter fc = new FileConverter();
	LinkedList<Sequence> seqs = busController
			.produceTrainSequence(fc.listOfDevices);
        int [] testsequencelength = {10, 20, 30, 45, 60, 90, 120, 180, 240, 300};

	HMM mHMM;

	// METHODS IMPLEMENTATION
	public HMMWrapper() {

	}

	public void trainAndTestMultiplecellOneRun(int sequence_length) {
		System.out.println("trainAndTestMultiplecellOneRun(int sequence_length) " + sequence_length);
		// busController.ShowDistance(fc.listOfDevices);
		// what is this
		LinkedList<Sequence> seqs1 = busController
				.produceTrainSequence(fc.listOfDevices);
		LinkedList<Sequence> seqs2 = busController.produceTrainSequence(
				fc.listOfDevices, 1);
		LinkedList<Sequence> seqs3 = busController.produceTrainSequence(
				fc.listOfDevices, 2);
		HMM mHMM1 = new HMM();
		mHMM1.train(seqs1);
		HMM mHMM2 = new HMM();
		mHMM2.train(seqs2);
		HMM mHMM3 = new HMM();
		mHMM3.train(seqs3);

		File trainFolder = new File(CONSTANTS.TRAIN_DATA_FOLDER);
		File testFolder = new File(CONSTANTS.TEST_DATA_FOLDER);
		File resultFolder = new File(CONSTANTS.RESULT_FOLDER);

		if (!trainFolder.exists() || !trainFolder.isDirectory()) {
			System.out.println("Please recheck CONSTANTS.TRAIN_DATA_FOLDER at "	+ CONSTANTS.TRAIN_DATA_FOLDER);
			return;
		}

		if (!testFolder.exists() || !testFolder.isDirectory()) {
			System.out.println("Please recheck CONSTANTS.TEST_DATA_FOLDER at "	+ CONSTANTS.TEST_DATA_FOLDER);
			return;
		}

		if (!resultFolder.exists()) {
			resultFolder.mkdir();
			System.out.println("CONSTANTS.RESULT_FOLDER created at " + CONSTANTS.TEST_DATA_FOLDER);
		}

		// CREATE RESULT LOG FILE
		int resultfileindex = resultFolder.list().length;
		try {
			Writer out = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(CONSTANTS.RESULT_FOLDER + resultfileindex
							+ ".txt"), "UTF-8"));

			// TRAIN
			// mHMM.emitAll();
			//out.write(mHMM1.toString());
			//mHMM1.emitAll();
			final int nExample = 1000;
			int countFalse = 0;
			int countFalseElement = 0;
			int countFalseLast = 0;

			Random randomGenerator = new Random();

			for (int i = 0; i < nExample; i++) {
				// TODO: check seqs.size()
				int fileindex = randomGenerator.nextInt(seqs1.size()) - 1;
				if (fileindex == -1) {
					fileindex = 0;
				}
				int start = randomGenerator.nextInt(seqs1.get(fileindex).size()
						- sequence_length - 1);
				Sequence testdata1 = new Sequence();
				Sequence testdata2 = new Sequence();
				Sequence testdata3 = new Sequence();
				for (int j = start; j < start + sequence_length; j++) {
					testdata1.add(seqs1.get(fileindex).get(j));
					testdata2.add(seqs2.get(fileindex).get(j));
					testdata3.add(seqs3.get(fileindex).get(j));
				}
				System.out.println("TEST " + i);
				out.write("\nTEST" + i + "\n");
				Sequence tmp1 = mHMM1.predict(testdata1);
				Sequence tmp2 = mHMM2.predict(testdata2);
				Sequence tmp3 = mHMM3.predict(testdata3);

				int index = 0;
				for (SequenceElement e : tmp1.sequenceinFile) {

					if (e.State == tmp2.sequenceinFile.get(index).State)
						continue;
					if (e.State == tmp3.sequenceinFile.get(index).State)
						continue;
					if (tmp2.sequenceinFile.get(index).State == tmp3.sequenceinFile
							.get(index).State)
						e.State = tmp2.sequenceinFile.get(index).State;

					++index;
				}

				if (testdata1.compareto(tmp1) != 0) {
					++countFalse;
					countFalseElement += testdata1.compareto(tmp1);
					//System.out.println(testdata1.toString());
					out.write(testdata1.toString() + "\n");

					if (tmp1 != null) {
						//System.out.println(tmp1.toString());
						out.write(tmp1.toString() + "\n");
					}
				}
				if (testdata1.compareLast(tmp1) == false) {
					++countFalseLast;
				}

			}
			out.write("Number of test: " + nExample + "\n");
			out.write("Number of positive: " + (nExample - countFalse) + "\n");
			out.write("Accuracy: "
					+ (1.0 * (nExample - countFalse) / nExample * 100.0) + "\n");
			out.write("Number of Element in test: " + nExample
					* sequence_length + "\n");
			out.write("Number of positive: "
					+ (nExample * sequence_length - countFalseElement) + "\n");
			out.write("Accuracy: "
					+ (1.0 * (nExample * sequence_length - countFalseElement)
							/ (nExample * sequence_length) * 100.0) + "\n");
			out.write("CountFalseElement/CountFalse: " + 1.0
					* countFalseElement / countFalse + '\n');

			out.write("CountFalseLast: " + countFalseLast + '\n');
			out.write("CountFalseLast Accuracy: " + 100 * (1- 1.0 * countFalseLast
					/ nExample) + '\n');

			out.close();

		} catch (Exception e) {
			System.err.println(e.getMessage());
			e.printStackTrace();
		}

	}

	public void trainMainCell() {
		// HEADER ???
		
		mHMM = new HMM();
		mHMM.train(seqs);
		// Writer out;
		// try {
		// out = new BufferedWriter(new OutputStreamWriter(
		// new FileOutputStream(CONSTANTS.RESULT_FOLDER + "trainsequence.txt"),
		// "UTF-8"));
		// for (Sequence sequence : seqs) {
		// out.write(sequence.toString());
		// }
		// } catch (Exception e) {
		// e.printStackTrace();
		// }
		// mHMM.emitAll();

	}

	public void testWithPrinting(int sequence_length) {

		try {
			double[] result = testWithoutPrinting(sequence_length, true);
			System.out.println("Number of test: " + result[0] );
			System.out.println("Number of positive: " + result[1] );
			System.out.println("Accuracy: " + result[2] );
			System.out.println("Number of Element in test: " + result[3] );
			System.out.println("Number of positive: " + result[4] );
			System.out.println("Accuracy: " + result[5] );
			System.out.println("CountFalseElement/CountFalse: " + result[6]);
			System.out.println("CountFalseLast: " + result[7] );
			System.out.println("CountFalseLast Accuracy: " + result[8] );
		} catch (Exception e) {
			System.err.println(e.getMessage());
			e.printStackTrace();
		}

	}

	public double[] testWithoutPrinting(int sequence_length, boolean writeToFileEachRun) {
		// CREATE RESULT LOG FILE
		double[] result = new double[9];
		checkFolders();
		File resultFolder = new File(CONSTANTS.RESULT_FOLDER);
		int resultfileindex = resultFolder.list().length;
		try {

			// TRAIN
			// mHMM.emitAll();
			Writer out = null;
			
			if(writeToFileEachRun)
			{
				out = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(CONSTANTS.RESULT_FOLDER + resultfileindex
							+ ".txt"), "UTF-8"));
			}
			int countFalse = 0;
			int countFalseElement = 0;
			int countFalseLast = 0;
			Random randomGenerator = new Random();

			for (int i = 0; i < CONSTANTS.nExample; i++) {
				// TODO: check seqs.size()
				int fileindex = randomGenerator.nextInt(seqs.size()) - 1;
				if (fileindex == -1) {
					fileindex = 0;
				}
				int start = randomGenerator.nextInt(seqs.get(fileindex).size()
						- sequence_length - 1);
				Sequence testdata = new Sequence();
				for (int j = start; j < start + sequence_length; j++) {
					testdata.add(seqs.get(fileindex).get(j));
				}
				Sequence tmp2 = mHMM.predict(testdata);

				if (testdata.compareto(tmp2) != 0) {
					++countFalse;
					countFalseElement += testdata.compareto(tmp2);
					if (writeToFileEachRun)
					{
						out.write(testdata.toString() + "\n");
						out.write(tmp2.toString()+"\n" + "\n");
					}
				}

				if (testdata.compareLast(tmp2) == false) {
					++countFalseLast;
				}

			}

			result[0] = CONSTANTS.nExample;
			result[1] = (CONSTANTS.nExample - countFalse);
			result[2] = (1.0 * (CONSTANTS.nExample - countFalse) / CONSTANTS.nExample * 100.0);
			result[3] = CONSTANTS.nExample * sequence_length;
			result[4] = (CONSTANTS.nExample * sequence_length - countFalseElement);
			result[5] = (1.0 * (CONSTANTS.nExample * sequence_length - countFalseElement)
					/ (CONSTANTS.nExample * sequence_length) * 100.0);
			result[6] = (1.0 * countFalseElement / countFalse);
			result[7] = countFalseLast;
			result[8] = 100* (1-1.0 * countFalseLast / CONSTANTS.nExample);
			if (writeToFileEachRun)
			{
				out.write("Number of test: " + result[0] + "\n");
				out.write("Number of positive: " + result[1] + "\n");
				out.write("Accuracy: " + result[2] + "\n");
				out.write("Number of Element in test: " + result[3] + "\n");
				out.write("Number of positive: " + result[4] + "\n");
				out.write("Accuracy: " + result[5] + "\n");
				out.write("CountFalseElement/CountFalse: " + result[6]+ "\n");
				out.write("CountFalseLast: " + result[7] + "\n");
				out.write("CountFalseLast Accuracy: " + result[8] + "\n");
				out.flush();
				out.close();
			}
		
			return result;

		} catch (Exception e) {
			System.err.println(e.getMessage());
			e.printStackTrace();
		}

		return null;

	}

	public void testMultipleRuns(boolean writeToFileEachRun) {
		checkFolders();
		File resultFolder = new File(CONSTANTS.RESULT_FOLDER);
		int resultfileindex = resultFolder.list().length;
		Writer out = null;

		// file header
		try {
			out = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(CONSTANTS.RESULT_FOLDER + resultfileindex
							+ "_50runs.txt"), "UTF-8"));

			out.write("i \t Number of test \t Number of positive \t Accuracy \t Number of Element in test \t "
					+ "Number of positive: \t Accuracy: \t CountFalseElement/CountFalse \t CountFalseLast: \t CountFalseLast Accuracy\n");
		} catch (IOException e) {
			System.err.println(e.getMessage());
			e.printStackTrace();
		}

		// file contains
		try {
			for (int i = 0 ; i < testsequencelength.length; ++i) {
				System.out.println("Testing " + testsequencelength[i]);
				double[] result = testWithoutPrinting(testsequencelength[i], writeToFileEachRun);

				out.write(testsequencelength[i] + "\t" + result[0] + "\t" + result[1] + "\t"
						+ result[2] + "\t" + result[3] + "\t" + result[4]
						+ "\t" + result[5] + "\t" + result[6] + "\t"
						+ result[7] + "\t" + result[8] + "\n");
				out.flush();
				System.out.println("DONE Testing " + testsequencelength[i]);

			}
			out.close();
		} catch (IOException e) {
			System.err.println(e.getMessage());
			e.printStackTrace();
		}

	}

	// test cut velocity
	public void checkFolders() {
		// CREATE FOLDERS
		File trainFolder = new File(CONSTANTS.TRAIN_DATA_FOLDER);
		File testFolder = new File(CONSTANTS.TEST_DATA_FOLDER);
		File resultFolder = new File(CONSTANTS.RESULT_FOLDER);

		if (!trainFolder.exists() || !trainFolder.isDirectory()) {
			System.out.println("Please recheck CONSTANTS.TRAIN_DATA_FOLDER at "
					+ CONSTANTS.TRAIN_DATA_FOLDER);
			return;
		}

		if (!testFolder.exists() || !testFolder.isDirectory()) {
			System.out.println("Please recheck CONSTANTS.TEST_DATA_FOLDER at "
					+ CONSTANTS.TEST_DATA_FOLDER);
			return;
		}

		if (!resultFolder.exists()) {
			resultFolder.mkdir();
			System.out.println("CONSTANTS.RESULT_FOLDER created at " + CONSTANTS.TEST_DATA_FOLDER);
		}
	}
}
